# -*- coding: UTF-8 -*-
"""
@Project ：swift-forge-engine
@File    ：pagination.py
@IDE     ：PyCharm 
@Author  ：Tony.Gan
@Date    ：2025/1/18 21:53 
@Des     ：分页插件
"""
from __future__ import annotations

import json
import math

from typing import TYPE_CHECKING, Generic, Sequence, TypeVar

from fastapi import Depends, Query
from fastapi_pagination import pagination_ctx
from fastapi_pagination.bases import AbstractPage, AbstractParams, RawParams
from fastapi_pagination.ext.sqlalchemy import paginate
from fastapi_pagination.links.bases import create_links
from pydantic import BaseModel, Field

if TYPE_CHECKING:
    from sqlalchemy import Select
    from sqlalchemy.ext.asyncio import AsyncSession

T = TypeVar("T")
DataT = TypeVar("DataT")
SchemaT = TypeVar("SchemaT")


class _Params(BaseModel, AbstractParams):
    current: int = Query(1, ge=1, description="页码")
    pageSize: int = Query(20, gt=0, le=100, description="每页多少条")  # 默认 20 条记录

    def to_raw_params(self) -> RawParams:
        return RawParams(
            limit=self.pageSize,
            offset=self.pageSize * (self.current - 1),
        )


class _Links(BaseModel):
    first: str = Field(..., description='首页链接')
    last: str = Field(..., description='尾页链接')
    self: str = Field(..., description='当前页链接')
    next: str | None = Field(None, description='下一页链接')
    prev: str | None = Field(None, description='上一页链接')


class _PageDetails(BaseModel):
    items: Sequence[T] = Field([], description='当前页数据')
    total: int = Field(..., description='总条数')
    current: int = Field(..., description='当前页')
    pageSize: int = Field(..., description='每页数量')
    total_pages: int = Field(..., description='总页数')
    links: _Links


class _Page(_PageDetails, AbstractPage[T], Generic[T]):
    __params_type__ = _Params  # 使用自定义的Params

    @classmethod
    def create(
            cls,
            items: Sequence[T],
            total: int,
            params: _Params,
    ) -> _Page[T]:
        current = params.current
        pageSize = params.pageSize
        total_pages = math.ceil(total / pageSize)
        links = create_links(
            first={'current': 1, 'pageSize': pageSize},
            last={'current': f'{math.ceil(total / params.pageSize)}', 'pageSize': pageSize} if total > 0 else {
                'current': 1, 'pageSize': pageSize},
            next={'current': f'{current + 1}', 'pageSize': pageSize} if (current + 1) <= total_pages else None,
            prev={'current': f'{current - 1}', 'pageSize': pageSize} if (current - 1) >= 1 else None,
        ).model_dump()

        return cls(
            items=items,
            total=total,
            current=current,
            pageSize=pageSize,
            total_pages=total_pages,
            links=links,
        )


class _PageData(BaseModel, Generic[DataT]):
    page_data: DataT | None = None


async def paging_data(db: AsyncSession, select: Select, page_data_schema: SchemaT) -> dict:
    """
    :param db:
    :param select:
    :param page_data_schema:
    :return:
    """
    # 解决total计算错误的办法，同时消除笛卡尔积的警告
    from sqlalchemy import func
    count_query = select.with_only_columns(
        func.count(),  # 确保仅选择COUNT，避免多余列
        maintain_column_froms=True  # 核心参数：保留原始查询的FROM表（包括JOIN）
    ).order_by(None)  # 移除ORDER BY，避免影响计数性能

    _paginate = await paginate(
        db,
        select,
        count_query=count_query   # 直接使用优化后的查询
    )
    page_data = (_PageData[_Page[page_data_schema]](page_data=_paginate)).model_dump_json()
    return json.loads(page_data).get("page_data")


# 分页依赖注入
DependsPagination = Depends(pagination_ctx(_Page))
